use anyhow::{Context, Result};
use std::fs;
use tokio::fs as tokio_fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use tokio::signal::unix::{signal, SignalKind};
use tokio;
use fs2::FileExt;  // 引入扩展 trait，使 File 拥有 lock_exclusive 方法
use std::fs::File;
use tokio::time::interval;
use walkdir::WalkDir;
use tracing::{info,warn} ;
use crate::config::{Config, Mapping};
use crate::sync;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use libc::pid_t;
use tokio::sync::{broadcast , oneshot} ;
use tokio::sync::broadcast::Sender;

#[derive(Debug)]
pub struct Daemon {
    pid_path : PathBuf ,
    config_path : PathBuf ,
    interval_minutes : u64 ,
}


impl Daemon {
    /// 创建新的 Daemon 实例
    pub fn new( pid_path: PathBuf, config_path: PathBuf, interval_minutes: u64) -> Self {
        Self {
            pid_path : pid_path,
            config_path : config_path,
            interval_minutes : interval_minutes,
        }
    }
   

    pub fn start(&self) -> Result<()> {
        if self.is_running().is_ok() {
            println!("daemon {} 已在运行", self.read_pid()?);
            return Ok(());
        }

        // 确保 PID 文件所在的目录存在
        if let Some(parent) = self.pid_path.parent() {
            fs::create_dir_all(parent).context("创建 PID 文件目录失败")?;
        }

        // eg: ice daemon run <time interval>
        let child = std::process::Command::new(std::env::current_exe()?)
            .arg("daemon")
            .arg("run")
            .spawn()
            .context("启动 daemon 失败")?;

        self.write_pid_file(child.id())?;
        info!("daemon 已启动 (PID: {})", child.id());
        Ok(())
    }

    pub fn stop(&self) -> Result<()> {
        if ! self.is_running().is_ok() {
            println!("daemon 未运行");
            return Ok(()) ;
        }
        let pid = self.read_pid().context("读取pid失败")? ;
        // 发送终止信号
        unsafe {
            if libc::kill(pid , libc::SIGTERM) != 0 {
                anyhow::bail!("发送终止信号失败 (PID: {})", pid);
            }
        }
        self.remove_pid_file()?;
        Ok(())
    }

    /// 查看 daemon 运行状态
    pub fn status(&self) -> Result<()> {
        if  self.is_running().is_ok() {
            println!("daemon {} 已在运行", self.read_pid()?);
        } else {
            println!("daemon 未运行");
        }
        Ok(())
    }


    /// 运行 daemon 主循环（内部调用，由子进程执行）
    pub async fn run(&self) -> Result<()> {
        let (exit_sender , mut exit_receiver) = broadcast::channel(1) ;
        // 注册信号处理（优雅退出）
        self.setup_signal_handler(exit_sender).await;

        let mut interval = interval(Duration::from_secs(self.interval_minutes * 60));
        info!("daemon 开始运行，扫描间隔: {} 分钟", self.interval_minutes);

        // 首次扫描
        if let Err(e) = self.scan_and_cleanup().await {
            warn!("首次扫描任务出错: {}", e);
        }

        loop{
            tokio::select! {
                _ = interval.tick() => {
                    if let Err(e) = self.scan_and_cleanup().await {
                        warn!("扫描任务出错: {}", e);
                    }
                }
                _ = exit_receiver.recv() => {
                    break; // 直接退出循环
                }
            }
        }

        info!("daemon 已优雅退出");
        Ok(())
    }

    /// 扫描并清理过期目录
    async fn scan_and_cleanup(&self) -> Result<()> {
        let config_content = tokio_fs::read_to_string(&self.config_path)
            .await
            .with_context(|| format!("加载配置文件失败: {}", self.config_path.display()))?;
        let config: Config = toml::from_str(&config_content)
            .context("解析配置文件失败")?;
        let now = SystemTime::now();

        for mapping in config.mappings {
            if ! mapping.source.exists(){
                info!("目录{}已被彻底驱逐,跳过...",mapping.name) ;
                continue ;
            }
            if let Err(e) = self.process_mapping(&mapping, now).await {
                warn!("处理目录 {} 失败: {}", mapping.source.display(), e);
            }
        }
        Ok(())
    }
    /// 处理单个映射目录的过期逻辑
    async fn process_mapping(&self, mapping: &Mapping, now: SystemTime) -> Result<()> {
        if mapping.ttl == "never" {
            return Ok(());
        }

        let ttl_minutes: u64 = mapping.ttl
            .parse()
            .with_context(|| format!("TTL 格式错误: {}", mapping.ttl))?;
        let ttl_duration = Duration::from_secs(ttl_minutes * 60);

        let mtime = self.directory_modified_time(&mapping.source).await
            .with_context(|| format!("获取目录修改时间失败: {}", mapping.source.display()))?;

        if let Ok(elapsed) = now.duration_since(mtime) {
            if elapsed > ttl_duration {
                info!(
                    "目录 {} 已过期（{} 分钟），执行最后同步...",
                    mapping.source.display(),
                    ttl_minutes
                );
                sync::sync(&mapping, sync::Direction::Push, false)
                    .context("同步目录失败")?;

                info!("删除源目录: {}", mapping.source.display());
                tokio_fs::remove_dir_all(&mapping.source)
                    .await
                    .with_context(|| format!("删除目录失败: {}", mapping.source.display()))?;
                info!("删除{}完毕", mapping.name);
            }
        }
        Ok(())
    }

    /// 基于read_pid 封装判断是否运行
    fn is_running(&self) -> Result<bool>  {
       match self.read_pid() {
            Ok(pid) => Ok(true),
            Err(e) =>  Err(e),
        }
    }

    /// 读取 PID 文件中的进程 ID
    fn read_pid(&self) -> Result<libc::pid_t> {
        let content = fs::read_to_string(&self.pid_path)
            .with_context(|| format!("读取 PID 文件失败: {}", self.pid_path.display()))?;
        content
            .trim()
            .parse()
            .with_context(|| format!("PID 文件格式错误: {}", content))
    }


    /// 写入 PID 文件（带独占锁防止竞态）
    fn write_pid_file(&self, pid: u32) -> Result<()> {
        let mut file = File::create(&self.pid_path)
            .with_context(|| format!("创建 PID 文件失败: {}", self.pid_path.display()))?;

        // 获取独占锁防止并发写入
        file.lock_exclusive()
            .with_context(|| format!("锁定 PID 文件失败: {}", self.pid_path.display()))?;

        fs::write(&self.pid_path, pid.to_string())
            .with_context(|| format!("写入 PID 文件失败: {}", self.pid_path.display()))?;

        Ok(())
    }

    /// 删除 PID 文件
    fn remove_pid_file(&self) -> Result<()> {
        if self.pid_path.exists() {
            fs::remove_file(&self.pid_path)
                .with_context(|| format!("删除 PID 文件失败: {}", self.pid_path.display()))?;
        }
        Ok(())
    }
    /// 获取目录最新修改时间
    async fn directory_modified_time(&self, path: &Path) -> Result<SystemTime> {
        let path = path.to_owned() ;
        tokio::task::spawn_blocking( move || {
            let mut max_time = SystemTime::UNIX_EPOCH;
            for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
                let meta = entry.metadata()
                    .with_context(|| format!("获取文件元数据失败: {}", entry.path().display()))?;
                if let Ok(mtime) = meta.modified() {
                    if mtime > max_time {
                        max_time = mtime;
                    }
                }
            }
            Ok(max_time)
        }).await.context("等待目录遍历完成") ?
    }

    async fn setup_signal_handler(&self, mut exit_sender : Sender<()>) -> Result<()> {

        let mut sigterm = signal(SignalKind::terminate())
            .map_err(|e| anyhow::anyhow!("无法注册 SIGTERM 处理器: {}", e))?;
        let mut sigint = signal(SignalKind::interrupt())
            .map_err(|e| anyhow::anyhow!("无法注册 SIGINT 处理器: {}", e))?;

        tokio::spawn(async move {
            tokio::select! {
                _ = sigterm.recv() => {
                    info!("收到终止信号(SIGTERM)，准备退出...");
                }
                _ = sigint.recv() => {
                    info!("收到中断信号(SIGINT)，准备退出...");
                }
            }
            let _ = exit_sender.send(());
        });

        Ok(())
    }
}